/* eslint-disable */
import { cloneDeep, isString, isArray, isNumber } from 'lodash'
import {
  GridItemDataInterface,
  KeyValueStore,
} from '@/components/vueGridLayout/interfate/grid-Item-data-inteface'
import { getWidgetJson } from '@/config/setting-chart'
import {
  ChartDetailsEnum,
  ChartDataTypeEnum,
  chartTypes,
  GeoMapGlyphSettingMap,
  GeoMapEnableGlyphTypes,
  GeoMapAllRelationKeys,
  GeoMapHeatmapKeys,
  GeoMapPointKeys,
  GeoMapPolygonKeys,
  GeoMapLineKeys,
  GeoMapWorldKeys,
  CONFIGURATION_NAME,
  GRAPH_ARRAY,
  filterFormTypeName,
} from '@/config/contant'
import { dealMapData } from '@/components/studio/data/util'
import { removeNullEntries } from '@/util/recommend-visualization'
import moment from 'moment'
import { FilterColumnType } from '@/components/visualization/util'

/**
 * 判断是否为数值型
 * @param dataResult
 * @param aggrTypeAttribute
 */
function checkIsNumber(
  dataResult: any,
  aggrTypeAttribute: Array<any>
): boolean {
  const aggrTypeAttributeFind = dataResult.columns.find(
    (col: KeyValueStore) => col.text === aggrTypeAttribute[0]
  )
  return aggrTypeAttributeFind && aggrTypeAttributeFind.isNumber
}

export const transCharts = [
  `${ChartDetailsEnum.areaChart}`,
  `${ChartDetailsEnum.groupBarChart}`,
  `${ChartDetailsEnum.lineChart}`,
  `${ChartDetailsEnum.scatterplot}`,
  `${ChartDetailsEnum.stackBarChart}`,
]

/** 将drop的geomap改为gridItem需要的结构 */
export function TransDropGeoMapGridItem(gridItemSet: GridItemDataInterface) {
  const gridItem = cloneDeep(gridItemSet)
  // 必须显式标明activeLayerIndex
  gridItem.chartOptions.activeLayerIndex = 0
  const {
    glyphConfig: { geoLayer },
  } = gridItem.chartOptions
  switch (geoLayer[0]) {
    case GeoMapEnableGlyphTypes.point:
      {
        const config = Object.keys(GeoMapGlyphSettingMap.point).reduce(
          (rec, property) => {
            rec[property] = gridItem.chartOptions[property]
            return rec
          },
          {} as any
        )
        config.coordinateValue = gridItem.chartOptions.labelKey
        config.encodingValue = gridItem.chartOptions.valueKey[0].value
        config.value = gridItem.chartOptions.value
        gridItem.chartOptions.geoLayer = [
          {
            id: 0,
            type: gridItem.chartOptions.glyph,
            config,
          },
        ]
      }
      break
    case GeoMapEnableGlyphTypes.heatmap:
      {
        const config = Object.keys(GeoMapGlyphSettingMap.heatmap).reduce(
          (rec, property) => {
            rec[property] = gridItem.formData[property]
            return rec
          },
          {} as any
        )
        config.coordinateValue = gridItem.formData.labelKey
        config.encodingValue = gridItem.formData.valueKey[0].value
        config.value = gridItem.chartOptions.value
        gridItem.chartOptions.geoLayer = [
          {
            id: 0,
            type: gridItem.formData.glyph,
            config,
          },
        ]
      }
      break
    case GeoMapEnableGlyphTypes.line:
    case GeoMapEnableGlyphTypes.polygon:
      {
        const keySet = '$$linePath$$'
        const { geoCombineField, labelKey } = gridItem.chartOptions
        gridItem.chartOptions.geoLayer = [
          {
            type: gridItem.formData.glyph,
            id: 0,
            config: {
              coordinateValue: keySet,
              encodingValue: gridItem.formData.valueKey[0].value || '',
              encodingType: gridItem.formData.valueKey[0].value
                ? 'width'
                : 'none',
              polygonColor: gridItem.formData.polygonColor || '#FF0000',
              borderColor: gridItem.formData.borderColor || '#000000',
              borderWidth: gridItem.formData.borderWidth || 1,
              // filter((item: any) => ![undefined, null,''].includes(item[keySet]))
              value: gridItem.chartOptions.value.reduce(
                (rec: Array<any>, dataItem: any) => {
                  rec.push({
                    ...dataItem,
                    $$linePath$$:
                      geoCombineField.length > 0
                        ? geoCombineField.map((key: string[]) => [
                            dataItem[key[0]],
                            dataItem[key[1]],
                          ])
                        : labelKey.map((key: any) => dataItem[key].split(',')),
                  })
                  return rec
                },
                [] as Array<any>
              ),
            },
          },
        ]
      }
      break
    case GeoMapEnableGlyphTypes.world:
      const {
        reverseMappingType,
        labelKey,
        valueKey,
        selectColor,
        encodingType,
        subChartTrigger,
        subChartSize,
        valueSeparator,
      } = gridItem.chartOptions
      gridItem.chartOptions.geoLayer = [
        {
          // 传入geomap组件的各层参数
          type: gridItem.formData.glyph, // 该层类型
          id: 0, // 该层id，要求唯一
          config: {
            // 该层的参数，不同类型的layer参数不同，详见wiki
            coordinateValue: labelKey[0], // 经纬度列
            encodingValue: (valueKey[0] && valueKey[0].value) || '', // 用于编码的列
            selectColor,
            polygonColor: gridItem.formData.polygonColor || '#FF0000',
            borderColor: gridItem.formData.borderColor || '#ffffff',
            borderWidth: gridItem.formData.borderWidth || 1,
            encodingType, // 编码类型
            subChartTrigger,
            subChartSize,
            reverseMappingType,
            valueSeparator: gridItem.formData.valueSeparator || valueSeparator,
            value: removeNullEntries(gridItem.chartOptions.value).map(
              (item: any) => {
                // 去除空值
                // line和polygon 模式下是多个点连接，需要多个坐标点数组集合
                item[labelKey[0]] =
                  item[`$old$-${labelKey[0]}`] || item[labelKey[0]]
                return item
              }
            ),
          },
        },
      ]
      break
    default:
      break
  }
  return gridItem
}

const geoMapSyncKeys = [
  'title',
  'mapTheme',
  'geoCombineField',
  'pointType',
  'title',
  'tableName',
  'glyph',
  'dataId',
  'mappingType',
  'encodingType',
  'pointRadius',
  'pointColor',
  'polygonColor',
  'borderColor',
  'borderWidth',
  'coordinateValue',
]

/** 绑定数据 */
function bindGeoMapData(
  gridItem: GridItemDataInterface | null,
  dataResult: KeyValueStore
) {
  if (gridItem) {
    // 必须显式标明activeLayerIndex
    gridItem.chartOptions.activeLayerIndex = 0
    geoMapSyncKeys.forEach((settingKey) => {
      gridItem.chartOptions[settingKey] = gridItem.formData[settingKey]
    })
    // gridItem.chartOptions.isVisualization = true
    const { columns, data, extraData } = dataResult.data
    let dataParse = [] as Array<any>
    data.forEach((item: Array<any>) => {
      const dataItem = {} as any
      columns.forEach((column: any, columnIndex: number) => {
        if (
          column.name === gridItem.formData.labelKey &&
          isString(item[columnIndex]) &&
          /\,/.test(item[columnIndex])
        ) {
          dataItem[column.name] = item[columnIndex].split(',')
        } else {
          dataItem[column.name] = item[columnIndex]
        }
      })
      dataParse.push(dataItem)
    })

    // @ts-ignore
    dataParse = dealMapData(gridItem, dataParse, extraData?.coordinates)
    if (gridItem.formData.glyph !== GeoMapEnableGlyphTypes.world) {
      dataParse = dataParse.filter((d) => d[gridItem.formData.labelKey[0]]) // 去除空值
    }
    switch (gridItem.formData.glyph) {
      case 'point':
        {
          const config = Object.keys(GeoMapGlyphSettingMap.point).reduce(
            (rec, property) => {
              rec[property] = gridItem.formData[property]
              return rec
            },
            {} as any
          )
          config.coordinateValue = gridItem.formData.labelKey[0]
          config.encodingValue =
            gridItem.formData.valueKey[0] && gridItem.formData.valueKey[0].value
          config.pointType = 'RoundPoint' // 点的类型
          config.encodingType = 'area'
          config.value = dataParse
          gridItem.chartOptions.geoLayer = [
            {
              type: gridItem.formData.glyph,
              id: 0,
              config,
            },
          ]
        }
        break
      case 'heatmap':
        {
          const config = Object.keys(GeoMapGlyphSettingMap.heatmap).reduce(
            (rec, property) => {
              rec[property] = gridItem.formData[property]
              return rec
            },
            {} as any
          )
          config.coordinateValue = gridItem.formData.labelKey[0]
          config.encodingValue = '' // gridItem.formData.valueKey[0].value
          config.heatmapRadius = gridItem.formData.heatmapRadius || 10
          config.value = dataParse
          gridItem.chartOptions.geoLayer = [
            {
              type: gridItem.formData.glyph,
              id: 0,
              config,
            },
          ]
        }
        break

      case GeoMapEnableGlyphTypes.line:
      case GeoMapEnableGlyphTypes.polygon:
        {
          const keySet = '$$linePath$$'
          const { geoCombineField, labelKey } = gridItem.chartOptions
          // filter((item: any) => ![undefined, null,''].includes(item[keySet])).
          gridItem.chartOptions.geoLayer = [
            {
              type: gridItem.formData.glyph,
              id: 0,
              config: {
                coordinateValue: keySet,
                encodingValue: gridItem.formData.valueKey[0].value || '',
                encodingType: gridItem.formData.valueKey[0].value
                  ? 'width'
                  : 'none',
                polygonColor: gridItem.formData.polygonColor || '#FF0000',
                borderColor: gridItem.formData.borderColor || '#000000',
                borderWidth: gridItem.formData.borderWidth || 1,
                value: dataParse.reduce((rec: Array<any>, dataItem: any) => {
                  rec.push({
                    ...dataItem,
                    $$linePath$$:
                      geoCombineField.length > 0
                        ? geoCombineField.map((key: string[]) => [
                            dataItem[key[0]],
                            dataItem[key[1]],
                          ])
                        : labelKey.map((key: any) =>
                            isString(dataItem[key])
                              ? dataItem[key].split(',')
                              : dataItem[key]
                          ),
                  })
                  return rec
                }, [] as Array<any>),
              },
            },
          ]
        }
        break
      case GeoMapEnableGlyphTypes.world:
        const {
          reverseMappingType,
          labelKey,
          valueKey,
          selectColor,
          encodingType,
          subChartTrigger,
          subChartSize,
          valueSeparator,
        } = gridItem.chartOptions
        gridItem.chartOptions.geoLayer = [
          {
            // 传入geomap组件的各层参数
            type: gridItem.formData.glyph, // 该层类型
            id: 0, // 该层id，要求唯一
            config: {
              // 该层的参数，不同类型的layer参数不同，详见wiki
              coordinateValue: labelKey[0], // 经纬度列
              encodingValue: (valueKey[0] && valueKey[0].value) || '', // 用于编码的列
              selectColor,
              polygonColor: gridItem.formData.polygonColor || '#FF0000',
              borderColor: gridItem.formData.borderColor || '#ffffff',
              borderWidth: gridItem.formData.borderWidth || 1,
              encodingType, // 编码类型
              subChartTrigger,
              subChartSize,
              reverseMappingType,
              valueSeparator:
                gridItem.formData.valueSeparator || valueSeparator,
              value: dataParse.map((item: any) => {
                // line和polygon 模式下是多个点连接，需要多个坐标点数组集合
                item[labelKey[0]] =
                  item[`$old$-${labelKey[0]}`] || item[labelKey[0]]
                return item
              }),
            },
          },
        ]
        break
      default:
        break
    }
  }
  return gridItem
}

/**
 * 绑定数据
 * @param gridItem
 * @param dataResult
 */
export function bindChartData(
  gridItem: GridItemDataInterface | null,
  dataResult: KeyValueStore
): GridItemDataInterface | null {
  if (gridItem) {
    if (isString(gridItem.chartType) && /\|/.test(gridItem.chartType)) {
      gridItem.chartType = gridItem.chartType.split('|')
    }
  }
  const result = cloneDeep(gridItem)
  if (result) {
    // if (result?.chartType[1] === 'geographicMap') {
    //   return bindGeoMapData(result, dataResult)
    // }
    let xAxisAttribute: Array<string | number> = []
    // const yAxisAttribute:Array<string|number> = []
    // const aggrTypeAttribute: Array<string | number> = []
    result.dataQuery = dataResult.query
    const { widgetJson } = dataResult.query.data
    if (!result.chartOptions) {
      result.chartOptions = {}
    }
    result.chartOptions.value = [] as Array<any>

    if (dataResult.data.length > 4000) {
      result.showChart = false
      result.statusText = '数据量超限'
    } else {
      const { keys, values, groups = [] } = widgetJson.config
      xAxisAttribute = keys
        .concat(groups)
        .map((item: { col: string }) => item.col)
      result.chartOptions = {
        ...result.chartOptions,
        ...dataResult.formData,
      }
      result.chartOptions.value = dataResult.data

      // result.chartOptions.labelKey = dataResult.formData.labelKey
      // const valueKeys = dataResult.formData.valueKey.reduce(
      //   (rec: Array<string>, item: KeyValueStore) => {
      //     const rec_ = item.value !== '' ? [...rec, item.value] : rec
      //     return rec_
      //   },
      //   []
      // )

      // result.chartOptions.valueKey = valueKeys[0] || 'valueKey'

      const valueKeys = (values as Array<{ col: string }>).map((v) => v.col)

      if (
        [undefined, ''].includes(result.chartOptions.xAxisAttribute) &&
        result.chartOptions.labelKey
      ) {
        result.chartOptions.xAxisAttribute = result.chartOptions.labelKey
      }

      if (groups.length > 0) {
        // renderOption.seriesField = groups[0].col
        result.chartOptions.colorField = groups[0].col
      } else {
        // delete renderOption.seriesField
        delete result.chartOptions.colorField
      }

      if (
        result.chartType !== 'scatterplot' &&
        !result.chartType.includes('scatterplot')
      ) {
        result.chartOptions.xAxisAttribute = result.chartOptions.labelKey // (aggrTypeAttribute && aggrTypeAttribute.length > 0  && !checkIsNumber(dataResult, aggrTypeAttribute))?  aggrTypeAttribute[0] : ( xAxisAttribute && xAxisAttribute.length > 0 ? xAxisAttribute[0] : '')
      }
      result.chartOptions.yAxisAttribute =
        valueKeys.length > 1 ? valueKeys : valueKeys[0] //  aggrTypeAttribute && aggrTypeAttribute.length > 0  && checkIsNumber(dataResult, aggrTypeAttribute)? aggrTypeAttribute[0] :  (yAxisAttribute && yAxisAttribute.length > 0 ?  yAxisAttribute[0] : '')
      // result.formData = dataResult.formData
      result.columns = dataResult.columns
      let chartTypeUsed: any = result.chartType

      if (isArray(result.chartType)) {
        ;[, chartTypeUsed] = result.chartType
      } else if (/\|/.test(result.chartType)) {
        ;[, chartTypeUsed] = result.chartType.split('|')
      }

      if (
        isArray(result.chartOptions.valueKey) &&
        [
          // `${ChartDetailsEnum.scatterplot}`,
          `${ChartDetailsEnum.groupBarChart}`,
          `${ChartDetailsEnum.areaChart}`,
          `${ChartDetailsEnum.lineChart}`,
          `${ChartDetailsEnum.stackBarChart}`,
        ].includes(chartTypeUsed)
      ) {
        result.chartOptions.yAxisAttribute = valueKeys
      }
      if (chartTypeUsed === `${ChartDetailsEnum.heatmapMatrix}`) {
        if (
          Array.isArray(result.chartOptions.colors) &&
          (result.chartOptions.colors as Array<string>)[0]
        ) {
          const color = (result.chartOptions.colors as Array<string>)[0]
          result.chartOptions.legendBasicColor = color
        }

        result.chartOptions.labelKeyY = keys[1].col
        result.chartOptions.valueKey = keys[1].col
        result.chartOptions.yAxisAttribute = keys[1].col
        result.chartOptions.heatMapDataKey = values[0].col

        result.chartOptions.axisCategoricalDataSelection = 'all'
        result.chartOptions.value.forEach(
          (dataItem: KeyValueStore, index: number) => {
            result.chartOptions.value[index][
              result.chartOptions.yAxisAttribute
            ] = `${
              result.chartOptions.value[index][
                result.chartOptions.yAxisAttribute
              ]
            }`
          }
        )
        // }
      }
      // 柱线混合的特殊属性
      if (
        isArray(valueKeys) &&
        valueKeys.length > 0 &&
        chartTypeUsed === `${ChartDetailsEnum.dualAxesChart}`
      ) {
        result.chartOptions.value = [
          result.chartOptions.value,
          result.chartOptions.value,
        ]
        if (valueKeys.length > 1) {
          result.chartOptions.yAxisAttribute = [
            valueKeys[0],
            valueKeys.slice(1),
          ]
          result.chartOptions.valueKey = valueKeys
        } else {
          // 只有1个度量时
          result.chartOptions.yAxisAttribute = [valueKeys[0], [valueKeys[0]]]
          result.chartOptions.valueKey = [valueKeys[0], valueKeys[0]]
        }
      }
      // if (
      //   isArray(valueKeys) &&
      //   chartTypeUsed === `${ChartDetailsEnum.parallelCoordinates}`
      // ) {
      //   result.chartOptions.parallelAxisNames = valueKeys
      //   // result.chartOptions.parallelAxisNames = result.formData.valueKey.reduce(
      //   //   (rec: Array<string>, item: KeyValueStore) => {
      //   //     const rec_ = item.value !== '' ? [...rec, item.value] : rec
      //   //     return rec_
      //   //   },
      //   //   []
      //   // )
      //   result.chartOptions.parallelAxisKeys =
      //     result.chartOptions.parallelAxisNames
      //   result.chartOptions.parallelGroupKey = result.chartOptions.labelKey
      //   result.chartOptions.value.forEach(
      //     (item: KeyValueStore, index: number) => {
      //       result.chartOptions.value[index][result.chartOptions.value] =
      //         result.chartOptions.value[index][result.chartOptions.value] ===
      //         null
      //           ? ''
      //           : `${
      //               result.chartOptions.value[index][result.chartOptions.value]
      //             }`
      //     }
      //   )
      //   result.chartOptions.xAxisAttribute = result.chartOptions.labelKey
      // }
      // 解决组件库labelKey不支持数字类型的问题
      if (
        [
          `${ChartDetailsEnum.pieChart}`,
          `${ChartDetailsEnum.donutChart}`,
        ].includes(chartTypeUsed)
      ) {
        result.chartOptions.value.forEach((dataItem: any) => {
          dataItem[result.chartOptions.labelKey] = isNumber(
            dataItem[result.chartOptions.labelKey]
          )
            ? `${dataItem[result.chartOptions.labelKey]}`
            : dataItem[result.chartOptions.labelKey]
        })
      }
      // areaRangeChart BoxLineChart
      if ([`${ChartDetailsEnum.areaRangeChart}`].includes(chartTypeUsed)) {
        const { confidence } = widgetJson.config
        const lowerKey = confidence.lower.value
        const upperKey = confidence.upper.value
        result.chartOptions.value = result.chartOptions.value.map(
          (dataItem: any) => {
            return {
              ...dataItem,
              range: [dataItem[lowerKey], dataItem[upperKey]],
            }
          }
        )
        result.chartOptions.valueKey = 'range'
        result.chartOptions.yAxisAttribute = result.chartOptions.yAxisAttribute.slice(
          0,
          -2
        )
      }

      result.showChart = true
    }
  }
  /**
   * 清空null key 的情况
   */
  result &&
    result.chartOptions.value.forEach((dataItem: KeyValueStore) => {
      Object.keys(dataItem).forEach((key) => {
        if (/^\[object/.test(key)) {
          delete dataItem[key]
        }
      })
    })
  if (
    result &&
    isString(result.chartOptions.xAxisAttribute) &&
    result.chartOptions.labelKeyFormat // Todo labelKeyFormat???????
  ) {
    result.chartOptions.value.forEach((valueItem: any) => {
      if (valueItem[result.chartOptions.xAxisAttribute]) {
        valueItem[result.chartOptions.xAxisAttribute] = moment(
          valueItem[result.chartOptions.xAxisAttribute]
        ).format(result.chartOptions.labelKeyFormat)
      }
    })
  }

  // if ( // 应该是走 chart的visibleItemsNumber
  //   result &&
  //   ['pieChart', 'donutChart'].includes(result.chartType[1]) &&
  //   result.formData &&
  //   result.formData.showNumber > 0 &&
  //   isArray(result.chartOptions.value)
  // ) {
  //   let newValue: Array<any> = result.chartOptions.value
  //   if (result.chartOptions.value.length > result.formData.showNumber) {
  //     newValue = result.chartOptions.value.slice(0, result.formData.showNumber)
  //     for (
  //       let i = result.formData.showNumber;
  //       i < result.chartOptions.value.length;
  //       i++
  //     ) {
  //       newValue[result.formData.showNumber - 1][
  //         result.chartOptions.valueKey
  //       ] += result.chartOptions.value[i][result.chartOptions.valueKey]
  //     }
  //     newValue[result.formData.showNumber - 1][result.chartOptions.labelKey] =
  //       '其他'
  //     result.chartOptions.value = newValue
  //   }
  //   result.chartOptions.visibleItemsNumber = newValue.length + 1 // 这里外部处理了，chart内部逻辑就先屏蔽掉
  //   result.chartOptions.labelTextMaxlength = 10
  //   // result.chartOptions.labelIsShow = false
  // }

  if (
    result &&
    ['barChart', 'groupBarChart', 'stackBarChart'].includes(
      result.chartType[1]
    ) &&
    result.chartOptions.axisCategoricalDataSelection === 'yAxis'
  ) {
    const xAttribute =
      typeof result.chartOptions.xAxisAttribute === 'string'
        ? [result.chartOptions.xAxisAttribute]
        : result.chartOptions.xAxisAttribute
    const yAttribute =
      typeof result.chartOptions.yAxisAttribute === 'string'
        ? [result.chartOptions.yAxisAttribute]
        : result.chartOptions.yAxisAttribute
    result.chartOptions.xAxisAttribute = yAttribute
    result.chartOptions.yAxisAttribute = xAttribute
  }
  return result
}

// 按照groupBy转置
export function transGroupData(
  gridItem: GridItemDataInterface
): GridItemDataInterface {
  const labelKey: string = gridItem.chartOptions.labelKey // "日期";
  const valueKey: string | Array<string> = gridItem.chartOptions.yAxisAttribute // "累计确诊";
  const groupBy: string = gridItem.formData.groupBy // "﻿国家地区"

  const mapsGroup: KeyValueStore = {}
  const mapsLabel: KeyValueStore = {}
  const data: Array<any> = gridItem.chartOptions.value
  if (isString(valueKey)) {
    data.forEach((dataItem) => {
      mapsLabel[dataItem[labelKey]] = {}
      mapsGroup[dataItem[groupBy]] = true
    })
    const result: Array<any> = []
    Object.keys(mapsLabel).forEach((label) => {
      const item: KeyValueStore = {}
      item[labelKey] = label
      Object.keys(mapsGroup).forEach((key) => {
        data.forEach((dataOrigin) => {
          if (dataOrigin[groupBy] === key) {
            if (item[key] === undefined) {
              item[key] = dataOrigin[valueKey]
            } else {
              item[key] += dataOrigin[valueKey]
            }
          }
        })
      })
      result.push(item)
    })
    gridItem.valueKey = Object.keys(mapsGroup)
    gridItem.chartOptions.yAxisAttribute = gridItem.valueKey
    gridItem.chartOptions.value = result
  } else {
    data.forEach((dataItem) => {
      mapsLabel[dataItem[labelKey]] = {}
      mapsGroup[dataItem[groupBy]] = true
    })
    const result: Array<any> = []
    const valueKeys: Array<string> = []
    Object.keys(mapsGroup).forEach((key) => {
      valueKey.forEach((valueKey_) => {
        valueKeys.push(`${key}(${valueKey_})`)
      })
    })
    const valueKeyAll: Array<string> = []
    Object.keys(mapsGroup).forEach((key) => {
      valueKey.forEach((keyValue) => {
        valueKeyAll.push(`${key}(${keyValue})`)
      })
    })

    Object.keys(mapsLabel).forEach((label) => {
      const item: KeyValueStore = {}
      item[labelKey] = label
      Object.keys(mapsGroup).forEach((key) => {
        data.forEach((dataOrigin) => {
          if (dataOrigin[groupBy] === key && dataOrigin[labelKey] === label) {
            valueKey.forEach((valueKey_) => {
              if (item[`${key}(${valueKey_})`] === undefined) {
                item[`${key}(${valueKey_})`] =
                  undefined === dataOrigin[valueKey_]
                    ? 0
                    : dataOrigin[valueKey_]
              } else {
                item[`${key}(${valueKey_})`] +=
                  undefined === dataOrigin[valueKey_]
                    ? 0
                    : dataOrigin[valueKey_]
              }
            })
            valueKeyAll.forEach((keyValue__) => {
              if (item[keyValue__] === undefined) {
                item[keyValue__] = 0
              }
            })
          }
        })
      })
      result.push(item)
    })
    gridItem.valueKey = valueKeys
    gridItem.chartOptions.yAxisAttribute = valueKeys
    gridItem.chartOptions.value = result
  }
  return gridItem
}

/**
 * 获取地图数据
 * @param labelKey
 * @param valueKey
 * @param data
 */
export function getMapItemData(
  labelKey: string,
  valueKey: string,
  featuresProp: KeyValueStore,
  data: Array<KeyValueStore>
): KeyValueStore {
  let result = null
  const props = cloneDeep(featuresProp)
  delete props.childNum
  const find = data.find((dataItem: KeyValueStore) => {
    return Object.values(props).includes(dataItem[labelKey])
  })
  if (find) {
    result = find[valueKey]
  }
  return {
    population: result,
    isFind: find ? true : false,
  }
}

/** 筛选器控件保存参数生成 */
const buildFilterFormParams = (
  gridItem: GridItemDataInterface,
  deleteId?: boolean
) => {
  const result: any = {
    data: {
      projectId: localStorage.getItem('projectId'),
      name: gridItem.chartOptions.title
        ? gridItem.chartOptions.title
        : Array.isArray(gridItem.chartType)
        ? gridItem.chartType[0]
        : gridItem.chartType,
      tid: gridItem.chartOptions.dataId,
      type: getWidgetType(gridItem),
      data: {
        widgetJson: {},
        chartType: gridItem.chartType,
        x: gridItem.x,
        y: gridItem.y,
        w: gridItem.w,
        h: gridItem.h,
        i: gridItem.i,
        chartOptions: JSON.parse(JSON.stringify(gridItem.chartOptions)),
        widgetType: gridItem.widgetType,
        interactionJson: gridItem?.interactionJson || {},
      },
    },
  }

  if (!deleteId) {
    ;(result.data as any).id = gridItem.widgetId
  }
  return result
}

/**
 * 差异BarChart是单独的一种类型
 * @param chartConfig chart 配置
 */
function parseDiffBarConfig(gridItem: GridItemDataInterface) {
  const {
    chartType,
    chartOptions: { diffBar },
  } = gridItem
  if (chartType[1] !== 'barChart') {
    return
  }
  gridItem.chartOptions.realChartType =
    diffBar === true ? 'diffBarChart' : 'barChart'
}

const getWidgetType = (gridItem: GridItemDataInterface) => {
  return gridItem.chartType === 'text'
    ? 'text'
    : gridItemIsMyComponent(gridItem)
    ? 'task_copy'
    : gridItemIsRecommend(gridItem)
    ? 'recommend'
    : checkIsFilterForm(gridItem)
    ? 'filterForm'
    : 'dataset'
}

/**
 * 保存参数
 */
export function buildSaveParameters(
  gridItem: GridItemDataInterface,
  deleteId?: boolean
): KeyValueStore {
  if (checkIsFilterForm(gridItem)) {
    return buildFilterFormParams(gridItem, deleteId)
  }
  const query = gridItem.dataQuery ? gridItem.dataQuery : {}
  parseDiffBarConfig(gridItem)
  let chartConfig = JSON.parse(JSON.stringify(gridItem.chartOptions))
  if (!chartConfig.titleHeight) {
    const defaultConfig = {}
    chartConfig = { ...chartConfig, ...defaultConfig }
  }
  if (
    gridItem.chartType !== CONFIGURATION_NAME &&
    gridItem.chartType !== 'text'
  ) {
    chartConfig.value = []
  }
  if (gridItem.chartOptions.subChartOptions) {
    chartConfig.subChartOptions.value = []
  }
  // geomap 清空geolayer内部数据
  if (gridItem.chartType[1] === 'geographicMap') {
    if (chartConfig.geoLayer && chartConfig.geoLayer.length > 0) {
      chartConfig.geoLayer.forEach((layer: any, index: number) => {
        chartConfig.geoLayer[index].config.value = []
      })
    }
  }
  const result = {
    data: {
      projectId: localStorage.getItem('projectId'),
      // 'id': gridItem.widgetId,
      // 'randomId': Math.floor(Math.random()* 10000),
      name: gridItem.chartOptions.title
        ? gridItem.chartOptions.title
        : Array.isArray(gridItem.chartType)
        ? gridItem.chartType[0]
        : gridItem.chartType,
      tid: gridItem.chartOptions.dataId,
      type: getWidgetType(gridItem),
      data: {
        // formData: gridItem.formData,
        widgetJson: query.data
          ? query.data.widgetJson
          : getWidgetJson(gridItem.chartOptions),
        chartType: gridItem.chartType,
        x: gridItem.x,
        y: gridItem.y,
        w: gridItem.w,
        h: gridItem.h,
        i: gridItem.i,
        chartOptions: chartConfig,
        widgetType: gridItem.widgetType,
        interactionJson: gridItem?.interactionJson || {},
      },
    },
  }
  if (!deleteId) {
    ;(result.data as any).id = gridItem.widgetId
  }
  if (GRAPH_ARRAY.includes(gridItem.chartType[1])) {
    // graph 数据集没有对应的id，统一删除掉
    delete result.data.tid
  }
  return result
}

/**
 * 判断是否为我的组件
 * @param gridItem
 */
export function gridItemIsMyComponent(
  gridItem: GridItemDataInterface
): boolean {
  return (
    gridItem &&
    gridItem.formData &&
    (gridItem.formData.dataType === ChartDataTypeEnum.myComponent ||
      gridItem.formData.dataType === 'task')
  )
}

export function gridItemIsRecommend(gridItem: GridItemDataInterface): boolean {
  return (
    gridItem &&
    gridItem.formData &&
    gridItem.formData.dataType === ChartDataTypeEnum.recommend
  )
}

export function gridItemIsDataClean(gridItem: GridItemDataInterface): boolean {
  return (
    gridItem && gridItem.formData && gridItem.formData.dataType === 'tclean'
  )
}

export function getGridItemType(gridItem: GridItemDataInterface) {
  if (gridItemIsMyComponent(gridItem)) {
    return 'task'
  } else if (gridItemIsRecommend(gridItem)) {
    return 'recommend'
  } else if (gridItemIsDataClean(gridItem)) {
    return 'tclean'
  }
  return 'dataset'
}

/** 根据当前glyph修改配置项的显示与隐藏 */
export function GeoMapGlyphChangedCallback(
  glyph: string,
  formItems: Array<any>
) {
  switch (glyph) {
    case `${GeoMapEnableGlyphTypes.heatmap}`:
      {
        formItems.forEach((item, index) => {
          // 组件目前glyph切换有问题，先禁用切换
          if (item.name === 'glyph') {
            formItems[index].props.disabled = true
          }
          if (GeoMapAllRelationKeys.includes(item.name)) {
            formItems[index].isHide = true
          }
          if (GeoMapHeatmapKeys.includes(item.name)) {
            formItems[index].isHide = false
          }
        })
      }
      break
    case `${GeoMapEnableGlyphTypes.point}`:
      {
        formItems.forEach((item, index) => {
          // 组件目前glyph切换有问题，先禁用切换
          if (item.name === 'glyph') {
            formItems[index].props.disabled = true
          }
          if (GeoMapAllRelationKeys.includes(item.name)) {
            formItems[index].isHide = true
          }
          if (GeoMapPointKeys.includes(item.name)) {
            formItems[index].isHide = false
          }
        })
      }
      break
    case `${GeoMapEnableGlyphTypes.line}`:
      {
        formItems.forEach((item, index) => {
          // 组件目前glyph切换有问题，先禁用切换
          if (item.name === 'glyph') {
            formItems[index].props.disabled = true
          }
          if (GeoMapAllRelationKeys.includes(item.name)) {
            formItems[index].isHide = true
          }
          if (GeoMapLineKeys.includes(item.name)) {
            formItems[index].isHide = false
          }
        })
      }
      break
    case `${GeoMapEnableGlyphTypes.polygon}`:
      {
        // const hideKeys = ['labelKey', 'topK']
        formItems.forEach((item, index) => {
          // 组件目前glyph切换有问题，先禁用切换
          if (item.name === 'glyph') {
            formItems[index].props.disabled = true
          }
          if (GeoMapAllRelationKeys.includes(item.name)) {
            formItems[index].isHide = true
          }
          if (GeoMapPolygonKeys.includes(item.name)) {
            formItems[index].isHide = false
          }
        })
      }
      break
    case `${GeoMapEnableGlyphTypes.world}`:
      {
        // const hideKeys = ['labelKey', 'topK']
        formItems.forEach((item, index) => {
          // 组件目前glyph切换有问题，先禁用切换
          if (item.name === 'glyph') {
            formItems[index].props.disabled = true
          }
          if (GeoMapAllRelationKeys.includes(item.name)) {
            formItems[index].isHide = true
          }
          if (GeoMapWorldKeys.includes(item.name)) {
            formItems[index].isHide = false
          }
        })
      }
      break
    default:
      break
  }
}

/** 可以解析为数值的字段类型 */
export const numberTypes = new Set([
  'smallint',
  'decimal',
  'integer',
  'bigint',
  'numberic',
  'real',
  'double precision',
  'serial',
  'bigserial',
  'decimal',
  'int',
  'numeric',
  '',
])

/** 检测是否是筛选器组件 */
export const checkIsFilterForm = (gridItem: GridItemDataInterface) => {
  return Array.isArray(gridItem.chartType)
    ? gridItem.chartType.includes(filterFormTypeName)
    : gridItem.chartType === filterFormTypeName
}

/** 从筛选控件获取筛选数据 */
export const getFilterDataFromFilterForm = (
  gridItem: GridItemDataInterface
) => {
  const queryData = gridItem.chartOptions?.queryData || {}
  return checkIsFilterForm(gridItem)
    ? /** 这里没有做过滤是为了重置的时候判断有没有enable = false的同名筛选列 */
      (gridItem.chartOptions?.filterColumns || []).map((item: any) => {
        return {
          col: item.value,
          desc: item.colType,
          producer: 'filterForm',
          producerId: gridItem.widgetId,
          filterType: item.isNumber ? '[a,b]' : 'in',
          values: queryData[item.value],
        }
      })
    : []
}

/** 合并新的筛选
 * 1.过滤掉其他或以前的filterForm筛选项
 * 2.逐条比对新的filter选项，如果原本的filter内部存在该筛选项，如果新的筛选有值，则禁用旧的筛选项
 * 3.把新的有效筛选项逐条插入列表
 */
export const mergeFilterFormFilters = (
  oldFilter: FilterColumnType[],
  formFilter: FilterColumnType[]
) => {
  /** 过滤掉之前的筛选的列 */
  const result = oldFilter
    .filter((col: any) => {
      return col.producer === undefined
    })
    .map((item) => {
      // 启用所有的列筛选
      return {
        ...item,
        enabled: true,
      }
    })
  const appendColumns: FilterColumnType[] = []
  formFilter.forEach((colFrom) => {
    const find = result.findIndex((f) => f.col === colFrom.col)
    if (colFrom.values.length > 0) {
      // 找到列明相同的筛选
      if (find !== -1) {
        // 如果存在并且旧的列禁用该列
        result[find].enabled = false
      }
      appendColumns.push(colFrom)
    }
  })

  return [...result, ...appendColumns]
}

export function parseGraphEdgeWeight(gridItem: GridItemDataInterface) {
  const { value } = gridItem.chartOptions
  let minWeight = Number.MAX_SAFE_INTEGER
  let maxWeight = Number.MIN_SAFE_INTEGER
  if (value.edges && value.edges.length > 0) {
    value.edges.forEach((edge: KeyValueStore) => {
      minWeight = Math.min(minWeight, edge.weight)
      maxWeight = Math.max(maxWeight, edge.weight)
    })
  } else {
    minWeight = 0
    maxWeight = 0
  }
  gridItem.chartOptions.edgeWeightRange = [
    +minWeight.toFixed(3),
    +maxWeight.toFixed(3),
  ]
}

const checkGraphAttrTypeIsNumber = (type_: number) => {
  return [5, 4, -5, 6, 8, 3, 2].includes(type_)
}

const getGraphColumnMaxMin = (nodesOrLinks: any[], attr: string) => {
  const result = { min: Infinity, max: -Infinity } as any
  nodesOrLinks.forEach((n: any) => {
    n.attrs = n.attrs || []
    const attrInfo = n.attrs.find((c: any) => c.key === attr)
    if (attrInfo) {
      result.min = Math.min(attrInfo.value, result.min)
      result.max = Math.max(attrInfo.value, result.max)
    }
  })
  return result
}

/** graph 数据字段末尾标识，用于区分是node字段还是link字段 */
export enum GraphColumnEndWith {
  node = '[node]',
  link = '[link]',
}

/** 获取列信息 */
export const getGraphDataColumns = (
  data: any,
  startNode: any,
  projectId: string | number
) => {
  const { nodes = [] as any[], links = [] as any[] } = data || {}
  const nodesAttrs = nodes[0]?.attrs || []
  const linksAttrs = links[0]?.attrs || []
  const result = { nodeColumns: [] as any[], linkColumns: [] as any[] }
  result.nodeColumns = nodesAttrs.map((item: any) => {
    const isNumber = checkGraphAttrTypeIsNumber(item.type)
    const maxMin = isNumber
      ? getGraphColumnMaxMin(data.nodes || [], item.key)
      : {}
    return {
      isGraphColumn: true,
      tableName: startNode.value,
      projectId,
      colGroup: 'node',
      label: item.key,
      value: `${item.key}${GraphColumnEndWith.node}`,
      colType: item.type,
      isNumber,
      ...maxMin,
    }
  })
  result.linkColumns = linksAttrs.map((item: any) => {
    const isNumber = checkGraphAttrTypeIsNumber(item.type)
    const maxMin = isNumber
      ? getGraphColumnMaxMin(data.links || [], item.key)
      : {}
    return {
      isGraphColumn: true,
      tableName: startNode.value,
      colGroup: 'link',
      projectId,
      label: item.key,
      value: `${item.key}${GraphColumnEndWith.link}`,
      colType: item.type,
      isNumber,
      ...maxMin,
    }
  })
  return result
}

/** 检测节点或关系数据是否匹配filter条件 */
const checkIsMatch = (node: any, filterKeys: any[], flterMap: any) => {
  if (!node.attrs) {
    return false
  }
  let tag = true
  for (let attr in node.attrs) {
    const { value, key } = node.attrs[attr]
    if (filterKeys.includes(key)) {
      switch (flterMap[key].mode) {
        case 'in':
          if (
            !(
              Array.isArray(flterMap[key].value) &&
              (flterMap[key].value as any[]).includes(value)
            )
          ) {
            tag = false
          }
          break
        case '[a,b]':
          if (
            !(
              Array.isArray(flterMap[key].value) &&
              isNumber(flterMap[key].value[0]) &&
              isNumber(flterMap[key].value[1]) &&
              isNumber(value) &&
              value >= flterMap[key].value[0] &&
              value <= flterMap[key].value[1]
            )
          ) {
            tag = false
          }
        default:
          break
      }
    }
  }
  return tag
}

/** 前端筛选graph数据 */
export const filterGraphData = (filterFormJson: any, valueSet: any) => {
  const { nodes, links } = valueSet
  const result = {
    nodes: nodes || [],
    links: links || [],
  }
  const enableValues = new Set([undefined, true])
  const filters: FilterColumnType[] = (
    filterFormJson || []
  ).filter((item: FilterColumnType) => enableValues.has(item.enabled))
  const nodesFilterMap = {} as any
  const linkFilterMap = {} as any
  filters.forEach((item) => {
    if (item.col.endsWith(GraphColumnEndWith.node)) {
      nodesFilterMap[item.col?.replace(GraphColumnEndWith.node, '')] =
        item.values?.length > 0
          ? {
              mode: item.filterType,
              value: item.values,
            }
          : undefined
    } else if (item.col.endsWith(GraphColumnEndWith.link)) {
      linkFilterMap[item.col?.replace(GraphColumnEndWith.link, '')] =
        item.values?.length > 0
          ? {
              mode: item.filterType,
              value: item.values,
            }
          : undefined
    }
  })

  const nodeFilterKeys = Object.keys(nodesFilterMap).filter(
    (key) => nodesFilterMap[key] !== undefined
  )
  const linkFilterKeys = Object.keys(linkFilterMap).filter(
    (key) => linkFilterMap[key] !== undefined
  )
  result.nodes =
    nodeFilterKeys.length === 0
      ? nodes
      : nodes.filter((node: any) => {
          return checkIsMatch(node, nodeFilterKeys, nodesFilterMap)
        })
  result.links =
    linkFilterKeys.length === 0
      ? links
      : links.filter((link: any) => {
          return checkIsMatch(link, linkFilterKeys, linkFilterMap)
        })
  return result
}

/** 检测fiterForm 是否可以执行任务或重置 */
export const checkFilterFormIsEnabled = (gridItem: GridItemDataInterface) => {
  const { chartOptions = {} } = gridItem
  return (
    chartOptions.associationChart?.length > 0 &&
    chartOptions.filterColumns?.filter((d: any) => d.value !== '').length > 0
  )
}
